package com.study.modules.saas.service;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.study.common.constants.RedisCache;
import com.study.common.BusinessException;
import com.study.common.constants.BaseConstants;
import com.study.modules.saas.repository.dao.MenuDao;
import com.study.modules.saas.repository.enums.MenuCategory;
import com.study.modules.saas.repository.enums.MenuStatus;
import com.study.modules.saas.repository.enums.MenuType;
import com.study.modules.saas.repository.model.MenuDO;
import com.study.modules.saas.repository.model.RoleDO;
import com.study.security.domain.CurrentUser;
import com.study.security.utils.SecurityUtils;
import com.study.vo.MenuRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

/**
 * @Description: 菜单
 * @ClassName: MenuService
 * @Author: hupengming
 * @CreateTime: 2022-05-19 15:21
 */
@Service
public class MenuService {

    @Autowired
    MenuDao menuDao;

    @Autowired
    RoleService roleService;

    @Autowired
    RedisCache redisCache;

    /**
     * 创建菜单
     *
     * <p>
     * 1.根节点菜单必须是MenuType.MENU,父菜单id为0
     * 2.如果是列表菜单MenuType.LIST,path(路径不能为空的)
     * </p>
     *
     * @param request
     * @return
     */
    public int createMenu(MenuRequest request) {

        if (StringUtils.isEmpty(request.getName())) {
            throw new BusinessException("菜单名称不能为空");
        }

        Integer parentId = request.getParentId();

        if (MenuType.MENU.equals(request.getType())) {
            request.setParentId(BaseConstants.MENU_PARENT_DEFAULT_ID);
        }

        if (MenuType.LIST.equals(request.getType())) {
            if (StringUtils.isNotEmpty(request.getPath())) {
                throw new BusinessException("该菜单路径不能为空");
            }
        }

        String name = request.getName();
        this.getMenuByName(request.getParentId(), name, request.getCategory());

        if (parentId != null) {
            this.getMenuByParentId(parentId);
        }

        CurrentUser currentUser = SecurityUtils.getCurrentUser();

        MenuDO menuDO = new MenuDO();
        menuDO.setName(request.getName());
        menuDO.setOrderNum(request.getOrderNum());
        menuDO.setParentId(request.getParentId());
        menuDO.setPath(request.getPath());
        menuDO.setType(request.getType());
        menuDO.setCategory(request.getCategory());
        menuDO.setPermission(request.getPermission());
        menuDO.setCreatedBy(currentUser.getUuid());
        menuDO.setCreatedAt(new Date());
        menuDO.setRemark(request.getRemark());

        int insert = menuDao.insert(menuDO);
        if (insert > 0) {
            // TODO 添加缓存
            String menu = JSONObject.toJSONString(menuDO);
            redisCache.setMenu(menuDO.getId(), menu);
        }
        return insert;
    }

    /**
     * 检查父菜单是否存在
     *
     * @param parentId
     */
    public void getMenuByParentId(Integer parentId) {

        MenuDO menuDO = menuDao.selectOne(new LambdaQueryWrapper<MenuDO>()
                .eq(MenuDO::getId, parentId)
                .eq(MenuDO::getParentId, BaseConstants.MENU_PARENT_DEFAULT_ID)
                .eq(MenuDO::getStatus, MenuStatus.ENABLE)
                .isNull(MenuDO::getDeletedAt));
        if (ObjectUtil.isNull(menuDO)) {
            throw new BusinessException("没有找到父菜单");
        }

        if (StringUtils.isNotEmpty(menuDO.getPath())) {
            throw new BusinessException("父节点存在路径 不允许绑定子菜单");
        }

    }

    /**
     * 检查菜单名称是否重复
     *
     * @param parentId
     * @param name
     * @param category
     */
    public void getMenuByName(Integer parentId, String name, MenuCategory category) {

        MenuDO menuDO = menuDao.selectOne(new LambdaQueryWrapper<MenuDO>()
                .eq(MenuDO::getParentId, parentId)
                .eq(MenuDO::getName, name)
                .eq(MenuDO::getStatus, MenuStatus.ENABLE)
                .eq(MenuDO::getCategory, category)
                .isNull(MenuDO::getDeletedAt));

        if (ObjectUtil.isNotNull(menuDO)) {
            throw new BusinessException("菜单已存在");
        }

    }


    /**
     * 根据roleId找到所属所有菜单id
     *
     * @param roleId
     * @return
     */
    public List<Integer> getRoleMenuIds(Integer roleId) {
        return menuDao.selectMenuIdByRoleId(roleId);
    }

    /**
     * 根据菜单ids查找菜单
     *
     * @param menuIds
     * @return
     */
    public List<MenuDO> getMenuList(List<Integer> menuIds) {
        return menuDao.selectMenuByIds(menuIds);
    }


    /**
     * 菜单分页
     *
     * @param button
     * @return
     */
    public List<MenuDO> getMenuPage(boolean button, String name, MenuType type) {
        List<MenuDO> menuDOS = menuDao.selectList(new LambdaQueryWrapper<MenuDO>()
                .eq(MenuDO::getStatus, MenuStatus.ENABLE)
                .eq(ObjectUtil.isNotNull(type), MenuDO::getType, type)
                .like(StringUtils.isNotBlank(name), MenuDO::getName, name)
                .isNull(MenuDO::getDeletedAt));
        if (button) {
            return this.menuChild(menuDOS);
        }
        return menuDOS;
    }


    /**
     * 菜单列表树形结构组装
     *
     * @param menus
     * @return
     */
    public List<MenuDO> menuChild(List<MenuDO> menus) {
        List<MenuDO> resultList = new ArrayList<>();
        for (Iterator<MenuDO> iterator = menus.iterator(); iterator.hasNext(); ) {
            MenuDO menuDO = iterator.next();
            if (BaseConstants.MENU_PARENT_DEFAULT_ID.equals(menuDO.getParentId())) { // 所有的父节点
                recursionMenu(menus, menuDO);
                resultList.add(menuDO);
            }
        }
        return resultList;
    }

    /**
     * 递归列表
     * <p>
     * 递归的意义：查找子节点
     * </p>
     *
     * @param list   全部菜单列表
     * @param menuDO 父节点菜单
     */
    public void recursionMenu(List<MenuDO> list, MenuDO menuDO) {
        List<MenuDO> menuDoChild = getMenuDoChild(list, menuDO);
        menuDO.setChildList(menuDoChild);
        for (MenuDO aDo : menuDoChild) {
            if (checkNexChild(list, aDo)) {
                recursionMenu(list, aDo);
            } else {
                aDo.setLeft(Boolean.TRUE);
            }
        }
    }


    /**
     * 是否有孩子节点
     *
     * @param list
     * @param menuDO
     * @return
     */
    public boolean checkNexChild(List<MenuDO> list, MenuDO menuDO) {
        return getMenuDoChild(list, menuDO).size() > 0 ? true : false;
    }


    /**
     * 获取父节点的孩子节点
     *
     * @param list   所有菜单列表
     * @param menuDO 父节点对象
     */
    public List<MenuDO> getMenuDoChild(List<MenuDO> list, MenuDO menuDO) {
        List<MenuDO> childList = new ArrayList<>();
        Iterator<MenuDO> iterator = list.iterator();
        while (iterator.hasNext()) {
            MenuDO next = iterator.next();
            if (next.getParentId().equals(menuDO.getId())) {
                childList.add(next);
            }
        }
        return childList;
    }

    /**
     * 菜单下拉框
     *
     * @param tree
     */
    public List<MenuDO> choose(boolean tree) {

        MenuCategory category = SecurityUtils.getCurrentUser().isAdmin() ? MenuCategory.ADMIN : MenuCategory.USER;

        List<MenuDO> list = menuDao.selectList(new LambdaQueryWrapper<MenuDO>()
                .eq(MenuDO::getCategory, category)
                .eq(MenuDO::getStatus, MenuStatus.ENABLE)
                .isNull(MenuDO::getDeletedAt)
                .orderByAsc(MenuDO::getOrderNum));
        if (tree) {
            return this.menuChild(list);
        }
        return list;
    }

    /**
     * 当前用户全部菜单
     *
     * @param button
     * @return
     */
    public List<MenuDO> online(boolean button) {

        CurrentUser currentUser = SecurityUtils.getCurrentUser();
        String uuid = currentUser.getUuid();
        List<MenuDO> re = null;

        if (currentUser.isAdmin()) {
            re = menuDao.selectList(new LambdaQueryWrapper<MenuDO>()
                    .eq(MenuDO::getCategory, MenuCategory.ADMIN)
                    .eq(MenuDO::getStatus, MenuStatus.ENABLE)
                    .isNull(MenuDO::getDeletedAt));
        } else {
            RoleDO roleByUuid = roleService.getRoleByUuid(uuid);
            re = menuDao.selectMenuByRoleId(roleByUuid.getId());
        }

        if (button) {
            return this.menuChild(re);
        }

        return re;
    }

    /**
     * 删除菜单
     *
     * @param id
     * @return
     */
    public Integer deletedMenu(Integer id) {

        MenuDO menuDO = menuDao.selectOne(new LambdaQueryWrapper<MenuDO>()
                .eq(MenuDO::getId, id)
                .eq(MenuDO::getStatus, MenuStatus.ENABLE)
                .isNull(MenuDO::getDeletedAt));

        if (ObjectUtil.isNull(menuDO)) {
            throw new BusinessException("没有找到该菜单");
        }

        if (BaseConstants.MENU_PARENT_DEFAULT_ID.equals(menuDO.getParentId())) { //是否是父菜单

            int count = menuDao.selectCount(new LambdaQueryWrapper<MenuDO>()
                    .eq(MenuDO::getParentId, menuDO.getId())
                    .eq(MenuDO::getStatus, MenuStatus.ENABLE)
                    .isNull(MenuDO::getDeletedAt));
            if (count > 0) {
                throw new BusinessException("该菜单还存在子菜单");
            }
        } else {
            int i = menuDao.selectMenuByMenuId(menuDO.getId());
            if (i > 0) {
                throw new BusinessException("菜单还与其他角色绑定");
            }
        }
        int i = menuDao.deleteById(id);
        if (i > 0) {
            // TODO 删除缓存
            redisCache.deletedMenu(id);
        }
        return i;
    }


    /**
     * id查找菜单
     *
     * @param id
     * @return
     */
    public MenuDO getMenuById(Integer id) {

        return menuDao.selectOne(new LambdaQueryWrapper<MenuDO>()
                .eq(MenuDO::getId, id)
                .eq(MenuDO::getStatus, MenuStatus.ENABLE)
                .isNull(MenuDO::getDeletedAt));
    }


}
